home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / ccmd / ccmdmd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-01-03  |  17.5 KB  |  626 lines

  1. /*
  2.  Author: Andrew Lowry
  3.  
  4.  Columbia University Center for Computing Activities, July 1986.
  5.  Copyright (C) 1986, 1987, Trustees of Columbia University in the City
  6.  of New York.  Permission is granted to any individual or institution
  7.  to use, copy, or redistribute this software so long as it is not sold
  8.  for profit, provided this copyright notice is retained.
  9. */
  10.  
  11. /* Machine dependent code for Unix systems -- Use preprocessor
  12. ** conditionals for peculiarities of particular systems, but
  13. ** PLEASE -- Avoid nesting preprocessor conditionals!
  14. **/
  15.  
  16. #include "ccmdlib.h"
  17. #include "cmfncs.h"            /* and internal symbols */
  18.  
  19. extern int errno;
  20.  
  21.  
  22. /* cmrpjmp
  23. **
  24. ** Purpose:
  25. **   Automatic reparse handler, installed via the cmsetrp macro from
  26. **   ccmd.h.  If this handler is installed in a CSB for which a reparse
  27. **   is needed, it will perform a longjmp to restart execution at the
  28. **   point following the installing cmsetrp invocation.  This point
  29. **   should be following the call to cmini that set up parsing for
  30. **   the current command line, and before the comnd call for the first
  31. **   field in the command.
  32. **
  33. ** Input arguments: None
  34. ** Output arguments: None
  35. ** Returns: Nothing
  36. **/
  37.  
  38. jmp_buf cmrpjb;            /* global jump buffer for autoreparse */
  39.  
  40. cmrpjmp()
  41. {
  42.   longjmp(cmrpjb,1);        /* do the jump */
  43.   return(CMxNOAR);        /* if it returns, it failed */
  44. }
  45.  
  46.  
  47.  
  48. /* cmerjmp, cmerjnp
  49. **
  50. ** Purpose:
  51. **   Automatic parse error handler, much like the automatic reparse
  52. **   handler described above.  The cmseter macro should be invoked
  53. **   just prior to issuing a prompt.  When a parsing error
  54. **   subsequently occurs (that is, the parse function is about to
  55. **   return anything but CMxOK), cmperr will be called to print the
  56. **   error, and then execution will jump back to the site of the macro
  57. **   invocation.  When the automatic error handler is installed, the
  58. **   user program can ignore the codes returned by parse, since they
  59. **   will always be CMxOK.  CSB field _cmerr may be examined to see
  60. **   whether the the prior command line was terminated by an error,
  61. **   and if so, which error.
  62. **
  63. **   Note: Reparse situations will be handled by the error handler if
  64. **   no reparse handler has been installed.
  65. **
  66. **   Cmerjnp is the same as cmerjmp, except that the error message is
  67. **   not printed.
  68. **
  69. ** Input arguments: None.
  70. ** Output arguments: None.
  71. ** Returns: Nothing.
  72. **/
  73.  
  74. jmp_buf cmerjb;                /* global jump buffer */
  75.  
  76. cmerjmp(ret)
  77. int ret;                /* code that triggered the handler */
  78. {
  79.   cmperr(ret);                /* issue error message */
  80.   longjmp(cmerjb,1);            /* take the jump */
  81.   return(CMxNOAE);            /* failed */
  82. }
  83.  
  84. cmerjnp(ret)
  85. int ret;                /* code that triggered the handler */
  86. {
  87.   longjmp(cmerjb,1);            /* take the jump */
  88.   return(CMxNOAE);            /* failed */
  89. }
  90.  
  91.  
  92. #if BSD
  93. /*
  94.  * if handling nonblocking I/O, and a EWOULDBLOCK error comes up
  95.  * Then jump to the point set with a cmsetbl() call.
  96.  * The user then could wait for input.
  97.  */
  98.  
  99. jmp_buf cmbljb;                /* global jump buffer */
  100.  
  101. cmbljmp(ret)
  102. int ret;                /* code that triggered the handler */
  103. {
  104.   if (errno != EWOULDBLOCK)
  105.       return(ret);
  106.   longjmp(cmbljb,1);            /* take the jump */
  107.   return(CMxNOAE);            /* failed */
  108. }
  109. #endif
  110.  
  111.  
  112. /*
  113. ** Machine-dependent IO routines...  Generally, a file descriptor is
  114. ** supplied as an argument to the calls.
  115. **/
  116.  
  117. static int autowr;            /* TRUE if automatic wrap at eol */
  118. static int li;                /* lines on screen */
  119. static char tcapent[1024];        /* complete termcap entry */
  120. static char tcarea[100];        /* decoded termcap entries */
  121. static char *nl = "\n";
  122. static char *cr = "\r";
  123. static char *cl,*ce,*up;        /* pointers to decoded entries */
  124. int outc();                /* output routine for tputs */
  125.  
  126. char *tgetstr();            /* termlib routine returns string */
  127.  
  128. /* cmgetc - get a character from the input source.  Return standard return
  129.  * code. 
  130. */
  131.  
  132. int cmgetc(c,fd)
  133. char *c;                /* pointer where char is placed */
  134. FILE * fd;                /* input file descriptor */
  135. {
  136.   int cc;                /* value from read */
  137.  
  138.   if (cmcsb._cmoj != NULL)
  139.     cmflsh(cmcsb._cmoj);        /* flush pending output */
  140.   if (fd == NULL)            /* no file descriptor.  EOF */
  141.     return(CMxEOF);
  142.   *c = cc = getc(fd);
  143.   if (cc == EOF)
  144.     return(CMxEOF);            /* end of file */
  145.   else
  146.     return(CMxOK);            /* good read */
  147. }
  148.  
  149. /* cmputc - Output a single character to the terminal */
  150.  
  151. cmputc(c,fd)
  152. char c;                    /* char to output */
  153. FILE * fd;                /* output filedesc */
  154. {
  155.  
  156.   if (fd != NULL) {
  157.     putc(c,fd);
  158.     if (c == '\n') 
  159.       cmflsh(fd);
  160.   }
  161. }
  162.  
  163. /* cmputs - Output null-terminated string to the terminal */
  164.  
  165. cmputs(s,fd)
  166. char *s;                /* output string */
  167. FILE *fd;                /* output filedesc */
  168. {
  169.   while(*s != '\0')
  170.     cmputc(*s++,fd);
  171. }
  172.  
  173. /* cmcr - Move to the beginning of the current line */
  174.  
  175. cmcr(fd)
  176. FILE * fd;                /* output filedesc */
  177. {
  178.   cmputs(cr,fd);            /* use term specific sequence */
  179. }
  180.  
  181. /* cmnl - Output a newline sequence to the comman stream */
  182.  
  183. cmnl(fd)
  184. FILE *fd;                /* output filedesc */
  185. {
  186.   cmputs(nl,fd);            /* use term-specific sequence */
  187. }
  188.  
  189. /* cmflsh - flush output on fd */
  190. cmflsh(fd)
  191. FILE *fd;
  192. {
  193.   if (fd != NULL)
  194.     fflush(fd);
  195. }
  196.  
  197. /* cmwrap - Make sure that cursor wraps when it is required */
  198.  
  199. cmwrap(fd)
  200. FILE * fd;                /* output filedesc */
  201. {
  202.   if (!autowr)
  203.     cmnl(fd);                /* newline if not automatic */
  204. }
  205.  
  206. /* cmcls - Clear the screen.  Current IOJ value in the CSB is used for
  207. ** character output.  Only invoked if that IOJ is for a CRT terminal.
  208. ** Returns TRUE iff the operation succeeds.
  209. **/
  210.  
  211. int
  212. cmcls()
  213. {
  214.   if (cl == NULL)
  215.     return(FALSE);            /* no clear screen sequence */
  216.   else {
  217.     tputs(cl,li,outc);            /* output the clear sequence */
  218.     fflush(cmcsb._cmoj);
  219.     return(TRUE);
  220.   }
  221. }
  222.  
  223. /* cmceol - Clear to end of line.  Current IOJ value in the CSB is
  224. ** used for character output.  Only invoked if that IOJ is for a CRT
  225. ** terminal.  Returns TRUE iff the operation succeeds.
  226. **/
  227.  
  228. cmceol()
  229. {
  230.   if (ce == NULL)
  231.     return(FALSE);            /* no ceol sequence */
  232.   else {
  233.     tputs(ce,1,outc);            /* else do the operation */
  234.     return(TRUE);
  235.   }
  236. }
  237.  
  238. /* cmupl - Moves up on line in the display without changing column
  239. ** position.  Should not wrap to bottom of screen or cause destructive
  240. ** downward scrolling.  Current IOJ value in the CSB is used for
  241. ** character output.  Only invoked if that IOJ is for a CRT terminal.
  242. ** Returns TRUE if the operation succeeds.
  243. */
  244.  
  245. cmupl()
  246. {
  247.   if (up == NULL)
  248.     return(FALSE);            /* no upline sequence */
  249.   else {
  250.     tputs(up,1,outc);            /* else do the operation */
  251.     return(TRUE);
  252.   }
  253. }
  254.  
  255. /* cmcpos - Returns the current column position on the display.
  256. ** We just assume the ccmd package is correct, since there's
  257. ** no facility in termlib for extracting column position, and
  258. ** the price for never knowing the cursor position is too high
  259. ** (really ugly user interface due to many blank lines).
  260. **/
  261.  
  262. int
  263. cmcpos()
  264. {
  265.   return(cmcsb._cmcol);
  266. }
  267.  
  268. /* cmflush - Flush all pending input on the input source */
  269.  
  270. cmflush(fd)
  271. FILE *fd;
  272. {
  273.   if (fd != NULL) {
  274.     if (isatty(fileno(fd)))        /* if it's a terminal */
  275. #if SYSV
  276. #define TIOCFLUSH TCFLSH
  277. #endif
  278.       ioctl(fileno(fd),TIOCFLUSH,NULL);    /* flush input and output */
  279.     fd->_cnt = 0;            /* flush stdio IOBUF. */
  280.   }
  281. }
  282.  
  283. /* cmtset - Initialize the source terminal, as currently set in the
  284. ** CSB.  If the file is a terminal, a termcap entry is obtained and
  285. ** examined to see whether or not it is a hardcopy terminal.  If not,
  286. ** various control strings are read from the termcap entry and saved
  287. ** for screen operations.  In any case, terminals are placed in cbreak
  288. ** mode without echoing, and INT and TSTP signals are caught to
  289. ** prevent the terminal remaining in a funny state upon exit, and to
  290. ** place it back into the required state upon continuation.
  291. **/
  292.  
  293. cmtset()
  294. {
  295.   int ofd, ifd;
  296.   char *areap = tcarea;            /* pointer to termcap decoding area */
  297.   char *ttype;                /* terminal type */
  298.   char *gttype();
  299.  
  300.   if (cmcsb._cmoj != NULL)
  301.     ofd = fileno(cmcsb._cmoj);        /* input file descriptor */
  302.   if (cmcsb._cmij != NULL)
  303.     ifd = fileno(cmcsb._cmij);        /* output file designator */
  304.  
  305.   if (cmcsb._cmoj != NULL && isatty(ofd)) { /* check if it is a terminal */
  306.     cmcsb._cmflg |= CM_TTY;        /* yup */
  307.     ttype = gttype(ofd);        /* get the terminal type name */
  308.     if (tgetent(tcapent,ttype) != 1) {    /* get termcap entry */
  309.       cmcsb._cmflg &= ~CM_CRT;        /* no luck... assume hardcopy */
  310.       cmcsb._cmcmx = 79;        /* use default max column */
  311.       cmcsb._cmwrp = 79;        /* and wrap column */
  312.       nl = "\n";            /* and set default newline */
  313.       cr = "\r";            /* and return sequences */
  314.     }
  315.     else if (cmcsb._cmoj != NULL) {
  316.       if (tgetflag("hc"))         /* hardcopy indicated? */
  317.         cmcsb._cmflg &= ~CM_CRT;    /* yup, note it */
  318.       else {
  319.         cmcsb._cmflg |= CM_CRT;        /* else flag a crt */
  320.         cl = tgetstr("cl",&areap);    /* get clear screen sequence */
  321.         ce = tgetstr("ce",&areap);    /* and clear end-of-line */
  322.         up = tgetstr("up",&areap);    /* and upline sequence */
  323.       }
  324.       nl = tgetstr("nl",&areap);    /* alwasy get newline sequence */
  325.       if (nl == NULL)
  326.         nl = "\n";            /* default if not specified */
  327.       cr = tgetstr("cr",&areap);    /* get return sequence */
  328.       if (cr == NULL)
  329.         cr = "\r";            /* or set default */
  330.       autowr = tgetflag("am");        /* check for autowrap */
  331.       li = tgetnum("li");        /* get number of lines */
  332.       if (li == -1)            /* no such entry? */
  333.         li = 24;            /* use default */
  334.       cmcsb._cmcmx = tgetnum("co");    /* get col count */
  335.       if (cmcsb._cmcmx == -1)
  336.         cmcsb._cmcmx = 79;        /* use default if not given */
  337.       else
  338.         cmcsb._cmcmx--;            /* else drop to max col position */
  339.       cmcsb._cmwrp = cmcsb._cmcmx;    /* set up autowrap column */
  340.     }
  341.     if (cmcsb._cmij != NULL && isatty(ifd)) {
  342.       cmcsb._cmflg |= CM_TTY;        /* yup */
  343.       raw(ifd);                /* set up the terminal properly */
  344.       intson();                /* install our interrupt handlers */
  345.     }
  346.   }
  347.   else {
  348.     if (cmcsb._cmij != NULL && isatty(ifd)) {
  349.       raw(ifd);                /* set up the terminal properly */
  350.       intson();                /* install our interrupt handlers */
  351.     }
  352.     else {
  353.       cmcsb._cmflg &= ~(CM_TTY | CM_CRT); /* not a tty, so not a crt either */
  354.       cmcsb._cmcmx = 79;        /* and just use default width */
  355.     }
  356.   }
  357. }
  358.  
  359. /* cmtend - Clean up after prior input source */
  360.  
  361. cmtend()
  362. {
  363.   int fd;
  364.  
  365.   if (cmcsb._cmij != NULL) {
  366.     fd = fileno(cmcsb._cmij);        /* file desc to shut down */
  367.  
  368.     if (cmcsb._cmflg & CM_TTY) {
  369.       unraw(fd);            /* reset former tty params */
  370.       intsoff();            /* remove our interrupt handlers */
  371.     }
  372.   }
  373. }
  374.  
  375. /* gttype - Return terminal type name for given tty file descriptor 
  376. ** Auxiliary routine for cmtset
  377. **/
  378.  
  379. static char *
  380. gttype(fd)
  381. int fd;
  382. {
  383.   char *type;                /* type name of terminal */
  384.   char *name;                /* terminal name */
  385.   char *cname;                /* controlling terminal name */
  386.   int cttyfd;                /* controlling terminal file desc */
  387.   int ctty;                /* TRUE if fd is controlling tty */
  388.   FILE *typedb;                /* stream for ttytype database */
  389.   char typelin[80];            /* line from ttytype database */
  390.   char *typecp;                /* pointer into type db entry */
  391.   extern char *ttyname(), *getenv();
  392.  
  393.   cttyfd = open("/dev/tty",O_RDWR,0);    /* open the controlling tty */
  394.   if (cttyfd < 0)
  395.     ctty = FALSE;            /* bad open - assume not ctty */
  396.   else {
  397.     name = ttyname(fd);            /* get the terminal name */
  398.     cname = ttyname(cttyfd);        /* and controlling tty name */
  399.     if (strcmp(name,cname) == 0)        /* same? */
  400.       ctty = TRUE;            /* yup, it is ctrl tty */
  401.     else
  402.       ctty = FALSE;            /* nope, some other tty */
  403.     close(cttyfd);            /* no more use for this */
  404.   }
  405.                     /*  */
  406.   if (ctty) {                /* controlling terminal? */
  407.     type = getenv("TERM");        /* yup, use environment var */
  408.     if (type != NULL)
  409.       return(type);            /* give it back if successful */
  410.   }
  411.  
  412.   name += 5;                /* skip the "/dev/" prefix */
  413.   typedb = fopen("/etc/ttytype","r");     /* open type database */
  414.   if (typedb == NULL)
  415.     return("unknown");            /* give up if bad open */
  416.   while (fgets(typelin,80,typedb) != NULL) { /* scan the database */
  417.     typecp = typelin;
  418.     while ((*typecp++) != SPACE);    /* scan for space in entry */
  419.     *(typecp-1) = NULCHAR;        /* change it to null */
  420.     
  421.     if (strcmp(name,typecp) == 0) {    /* this our entry? */
  422.       fclose(typedb);            /* yup, shut database */
  423.       return(typelin);            /* and return type name */
  424.     }
  425.   }
  426.   fclose(typedb);            /* not found... close database */
  427.   return("unknown");            /* and give default */
  428. }
  429.  
  430. /* auxiliary routines to take terminals into and out of raw mode */
  431.  
  432. #ifdef BSD
  433. static struct sgttyb ttyblk, ttysav;    /* tty parameter blocks */
  434. static struct ltchars ltc,ltcsav;    /* local special chars for new  */
  435. #endif
  436.  
  437. #if SYSV
  438. static struct termio ttyblk, ttysav;
  439. #endif
  440.  
  441. /* raw - put the terminal into raw (actually cbreak) mode, turn off
  442. ** echoing and output translations, and extract the output speed for
  443. ** the termcap library.  On BSD unix systems, literal-next processing
  444. ** is also disabled.
  445. **/
  446.  
  447. static
  448. raw(fd)
  449. int fd;
  450. {
  451. #if SYSV 
  452.   ioctl(fd, TCGETA, &ttysav);
  453.   ttyblk = ttysav;
  454.   ttyblk.c_lflag &= ~(ICANON|ECHO);
  455.   ttyblk.c_cc[0] = 003;            /* interrupt char is control-c */
  456.   ttyblk.c_cc[4] = 1;
  457.   ttyblk.c_cc[5] = 1;
  458.   ioctl(fd,TCSETAW,&ttyblk);        /* set new modes . */
  459. #endif /* SYSV */
  460.  
  461. #if BSD
  462.   extern short ospeed;            /* declared in termlib */
  463.  
  464.   ioctl(fd,TIOCGETP,&ttysav);        /* get original parameters */
  465.   ttyblk = ttysav;            /* copy into new parameter block */
  466.   ospeed = ttysav.sg_ospeed;        /* save output speed for termlib */
  467.   ttyblk.sg_flags &= ~(RAW | ECHO | LCASE); /* no echoing or xlates */
  468. #ifdef undef
  469.   ttyblk.sg_flags |= CBREAK | CRMOD;    /* single character reads */
  470. #else
  471.   /* don't mess with CRMOD setting */
  472.   ttyblk.sg_flags |= CBREAK;        /* single character reads */
  473. #endif
  474.   ioctl(fd,TIOCSETN,&ttyblk);        /* set params, leave typeahead */
  475.   ioctl(fd,TIOCGLTC,<c);        /* get current local special chars */
  476.   ltcsav = ltc;                /* copy it for later restore */
  477.   ltc.t_lnextc = -1;            /* disable literal-next */
  478.   ioctl(fd,TIOCSLTC,<c);        /* set the new chars in place */
  479. #endif
  480. }
  481.  
  482. /* unraw - restore the tty modes in effect before raw was performed. */
  483.  
  484. static
  485. unraw(fd)
  486. int fd;
  487. {
  488. #if SYSV
  489.   ioctl(fd,TCSETAW, &ttysav);        /* put back saved params */
  490. #endif
  491. #ifdef BSD
  492.   ioctl(fd,TIOCSETP,&ttysav);        /* put back saved params */
  493.   ioctl(fd,TIOCSLTC,<csav);        /* restore local special chars */
  494. #endif
  495. }
  496.  
  497. /* outc - aux routine to be passed to termlib routines - output one char */
  498.  
  499. PASSEDSTATIC
  500. outc(c)
  501. char c;
  502. {
  503.   FILE *fd = cmcsb._cmoj;        /* get output filedesc */
  504.  
  505.   if (fd != NULL)
  506.     putc(c,fd);                /* do the write */
  507. }
  508.  
  509. /* intson - Install our interrupt handlers for INT and STOP, so
  510. ** any terminal settings we have installed will be undone before
  511. ** the program exits.  Any handlers that are already installed
  512. ** are left in place.  Those handlers should call cmdone if
  513. ** they expect to exit with the terminal set correctly.
  514. **/
  515.  
  516. #ifdef V7                /* used improve calls in version 7 */
  517. #define signal sigsys
  518. #endif
  519. #define mask(sig)   (1 << ((sig)-1))
  520.  
  521. static
  522. intson()
  523. {
  524.   int sighand();            /* forward decl of our handler */
  525.   int oldmask;                /* signal mask before we play */
  526.   int (*oldhand)();            /* old handler */
  527.  
  528. #ifdef SIGTSTP
  529.   oldmask = sigblock(mask(SIGINT) | mask(SIGTSTP));    /* hold these */
  530. #endif
  531.   oldhand = signal(SIGINT,sighand);    /* install our handler, get prior */
  532.   if (oldhand != SIG_DFL)        /* did they have something? */
  533.     signal(SIGINT,oldhand);        /* yup, leave it there */
  534. #ifdef SIGTSTP
  535.   oldhand = signal(SIGTSTP,sighand);    /* install ours for TSTP too */
  536.   if (oldhand != SIG_DFL)
  537.     signal(SIGTSTP,oldhand);        /* but leave theirs intact */
  538.   sigsetmask(oldmask);            /* now unblock the signals */
  539. #endif /* BSD */
  540. }
  541.  
  542.  
  543. /* intsoff - Remove our interrupt handlers.  If we remove something
  544. ** that isn't ours, put it back.
  545. **/
  546.  
  547. static
  548. intsoff()
  549. {
  550.   int sighand();            /* forward decl of our handler */
  551.   int oldmask;                /* sig mask prior to our diddling */
  552.   int (*oldhand)();            /* prior handler for a signal */
  553.  
  554. #ifdef SIGTSTP
  555.   oldmask = sigblock(mask(SIGINT) | mask(SIGTSTP));    /* block our sigs */
  556. #endif
  557.   oldhand = signal(SIGINT,SIG_DFL);    /* remove INT handler */
  558.   if (oldhand != sighand)
  559.     signal(SIGINT,oldhand);        /* replace if not ours */
  560. #ifdef SIGTSTP
  561.   oldhand = signal(SIGTSTP,SIG_DFL);    /* remove TSTP handler */
  562.   if (oldhand != sighand)
  563.     signal(SIGTSTP,oldhand);        /* replace if not ours */
  564.   sigsetmask(oldmask);            /* replace old signal mask */
  565. #endif
  566. }
  567.  
  568.  
  569. /* sighand - Handler for INT and TSTP signals.  We first fix
  570. ** the terminal to its normal settings, then remove our handler
  571. ** and generate whichever signal invoked us to get the default
  572. ** action.  If the program is continued, the terminal is adjusted
  573. ** again, and our handler is reinstalled.  (This should only happen
  574. ** with TSTP signals).
  575. **/
  576.  
  577. static
  578. sighand(sig,code,scp)
  579. int sig,code;                /* sig is all we care about */
  580. struct sigcontext *scp;
  581. {
  582.   int oldmask;                /* prior interrupt mask */
  583.   long getpid();            /* pids are long */
  584.  
  585.   cmtend();                /* fix the terminal */
  586. #ifdef BSD
  587.   oldmask = sigsetmask(0);        /* let our signal get through */
  588. #endif
  589. #ifdef SIGTSTP
  590.   if (sig == SIGTSTP)
  591.     cmnl(stdout);            /* move to new line for looks */
  592. #endif SIGTSTP
  593.   kill(getpid(),sig);            /* get the default action */
  594.   cmtset();                /* redo the terminal if continued */
  595. #ifdef BSD
  596.   sigsetmask(oldmask);            /* set mask back to before */
  597. #endif
  598. }
  599.  
  600.  
  601.  
  602. /* ctty, cttycls - Ctty opens the controlling terminal and returns a
  603. ** file descriptor for it.  After the first call, it just returns the
  604. ** file descriptor opened previously.  Cttycls closes the file
  605. ** descriptor opened by ctty, after which another call to ctty will
  606. ** open another one.
  607. **/
  608.  
  609. static int ttyfd = -1;
  610.  
  611. static int
  612. ctty()
  613. {
  614.   if (ttyfd == -1)
  615.     ttyfd = open("/dev/tty",O_RDWR,0);
  616.   return(ttyfd);
  617. }
  618.  
  619. static
  620. cttycls()
  621. {
  622.   if (ttyfd != -1)
  623.     close(ttyfd);
  624.   ttyfd = -1;
  625. }
  626.